package models

import (
	"crypto/md5"
	"fmt"
	"gitchina/kly_service/data"
	_ "gitchina/kly_service/errors"
	"gitchina/kly_service/util"

	"gitchina/kly_service/errors"
	"gitchina/kly_service/models/qpay"
	"github.com/pborman/uuid"
	"math"
)

type GatewayPay struct{}

func (gp *GatewayPay) PreTopUp(memberId int, amount float32) (ID, error) {
	return gp.prePay(PayItem{
		MemberId:    memberId,
		BillAmount:  amount,
		TotalAmount: amount,
		TypeId:      1,
		PayFor:      2,
		TransNo:     fmt.Sprintf("%x", md5.Sum([]byte(string(uuid.New())))),
	})
}

func (gp *GatewayPay) PostTopUp(p data.PaymentDto, i PostPayItem) (err error) {
	var w Wallet
	var platformBalanceDto data.PlatformBalanceDto
	repo := &data.PayRepo{}
	repo.BeginTx()
	defer func() {
		if err != nil {
			util.GetLogger().Error("[model-PostTopUp] - error%s", err.Error())
		}
		repo.EndTx(err == nil)
	}()
	w.repo = repo

	if err = repo.UpdatePaymentState(p.Id, i.PayOk, fmt.Sprintf("%s|%s", i.Message, i.CallbackData)); err == nil {
		if err = repo.UpdateTransFlowState(p.FlowId, i.PayOk, i.Message); err == nil {
			if i.PayOk {
				if err = repo.NewBankDetailRecord(data.BankDetailDto{
					MemberId: p.MemberId,
					FlowId:   p.FlowId,
					Amount:   p.TotalAmount,
					BindId:   0,
					TypeId:   1,
				}); err == nil {
					if platformBalanceDto, err = repo.GetPlatformBalanceById(1); err == nil {
						if err = repo.AddAndUpdatePlatformBalance(platformBalanceDto, p.FlowId, p.TotalAmount, 0); err == nil {
							w.TopUp(p.MemberId, 1, 0, p.FlowId, p.TotalAmount, 0, 0, false, "")
						}
					}
				}
			}
		}
	}
	return
}

func (gp *GatewayPay) PrePayDeposit(item PayItem) (ID, error) {
	return gp.prePay(item)
}

func (gp *GatewayPay) DoPayDeposit(payID ID) (interface{}, error) {
	return gp.doPay(payID, 0)
}

func (gp *GatewayPay) PostPayDeposit(p data.PaymentDto, i PostPayItem) (err error) {
	var pBalances []data.PlatformBalanceDto
	repo := &data.PayRepo{}
	repo.BeginTx()
	defer func() {
		if err != nil {
			util.GetLogger().Error("[model-PostPayDeposit] - error%s", err.Error())
		}
		repo.EndTx(err == nil)
	}()
	if err = repo.UpdatePaymentState(p.Id, i.PayOk, fmt.Sprintf("%s|%s", i.Message, i.CallbackData)); err == nil {
		if err = repo.UpdateTransFlowState(p.FlowId, i.PayOk, i.Message); err == nil {
			if i.PayOk {
				if err = repo.NewBankDetailRecord(data.BankDetailDto{
					MemberId: p.MemberId,
					FlowId:   p.FlowId,
					Amount:   p.TotalAmount,
					BindId:   0,
					TypeId:   1,
				}); err == nil {
					if pBalances, err = repo.GetPlatformBalances([]int{1, 5}); err == nil {
						if p.TotalAmount > 0 {
							if err = repo.AddAndUpdatePlatformBalance(pBalances[0], p.FlowId, p.TotalAmount, 0); err == nil {
								if p.DepositAmount > 0 {
									err = repo.AddAndUpdatePlatformBalance(pBalances[1], p.FlowId, p.DepositAmount, 0)
								}
							}
						}
					}
				}
			}
		}
	}

	return
}

func (gp *GatewayPay) PrePayWaybill(item PayItem) (ID, error) {
	return gp.prePay(item)
}

func (gp *GatewayPay) DoPayWaybill(payID ID) (interface{}, error) {
	return gp.doPay(payID, 1)
}

func (gp *GatewayPay) PostPayWaybill(p data.PaymentDto, i PostPayItem) (err error) {
	var pBalances []data.PlatformBalanceDto
	repo := &data.PayRepo{}
	repo.BeginTx()
	defer func() {
		if err != nil {
			util.GetLogger().Error("[model-PostPayDeposit] - error%s", err.Error())
		}
		repo.EndTx(err == nil)
	}()

	if err = repo.UpdatePaymentState(p.Id, i.PayOk, fmt.Sprintf("%s|%s", i.Message, i.CallbackData)); err == nil {
		if err = repo.UpdateTransFlowState(p.FlowId, i.PayOk, i.Message); err == nil {
			if i.PayOk {
				if err = repo.NewBankDetailRecord(data.BankDetailDto{
					MemberId: p.MemberId,
					FlowId:   p.FlowId,
					Amount:   p.TotalAmount,
					BindId:   0,
					TypeId:   1,
				}); err == nil {
					if pBalances, err = repo.GetPlatformBalances([]int{1, 5}); err == nil {
						if p.TotalAmount > 0 {
							if err = repo.AddAndUpdatePlatformBalance(pBalances[0], p.FlowId, p.TotalAmount, 0); err == nil {
								if p.BillAmount > 0 {
									err = repo.AddAndUpdatePlatformBalance(pBalances[1], p.FlowId, p.BillAmount, 0)
								}
							}
						}
					}
				}
			}
		}
	}
	return
}

func (gp *GatewayPay) Confirm(memberID, payID ID, params string) error {
	var prepo = new(data.PayRepo)

	_, _, _, mid, err := memberID.Decode()
	if err != nil {
		return errors.New(errors.MSG_Mbr_Invalid)
	}

	_, _, _, pid, err := payID.Decode()
	if err != nil {
		return errors.New(errors.MSG_Pay_Invalid_ID)
	}

	ok, info := prepo.GetQPay(int(pid))
	if ok {
		if info.MemberID != int(mid) {
			return errors.New(errors.MSG_Pay_Not_Same_Payer)
		}
		if info.State != 1 {
			return errors.New(errors.MSG_Pay_Error)
		}
	} else {
		return errors.New(errors.MSG_Pay_Error)
	}

	pd, err := prepo.GetPaymentDetailByPayID(int(pid))
	if err != nil {
		util.GetLogger().Error("[PayConfirm] - error:%s", err.Error())
		return errors.New(errors.MSG_Pay_Error)
	}

	card, err := prepo.GetQPayBankCard(int(mid), pd.CardId)
	if err != nil {
		util.GetLogger().Error("[PayConfirm] - error:%s", err.Error())
		return errors.New(errors.MSG_Pay_Error)
	}

	p := qpay.NewPayment()
	p.OrderID = string(payID)
	p.AgreeID = card.PartnerID
	p.Thpinfo = info.Params
	result, err := p.Confirm(params)
	if err != nil {
		util.GetLogger().Error("[PayConfirm] - error:%s", err.Error())
		return errors.New(errors.MSG_Pay_Error)
	}
	if result.OK() {
		prepo.SetQPayConfirmOK(int(pid), result.Undecoded())
	} else {
		prepo.UpdateQPayUndecoded(int(pid), result.Undecoded())
	}

	return nil
}

func (gp *GatewayPay) prePay(item PayItem) (paymentId ID, err error) {
	var flowId, payId int64
	repo := &data.PayRepo{}
	repo.BeginTx()
	defer func() {
		repo.EndTx(err == nil)
	}()
	remark := "付订金"
	if item.PayFor == 1 {
		remark = "付运费"
	}
	if flowId, err = repo.NewTransFlow(data.TransFlowDto{
		MemberId: item.MemberId,
		TransNo:  item.TransNo,
		Amount:   item.TotalAmount,
		TypeId:   item.TypeId,
		State:    0,
		Remark:   remark,
	}); err != nil {
		util.GetLogger().Error("[PayGateway-prePay] - error:%s", err.Error())
	} else {
		if payId, err = repo.NewPayment(data.PaymentDto{
			FlowId:          flowId,
			MemberId:        item.MemberId,
			WaybillId:       item.WaybillId,
			BillAmount:      item.BillAmount,
			InvoiceAmount:   item.InvoiceAmount,
			InsuranceAmount: item.InsuranceAmount,
			DepositAmount:   item.DepositAmount,
			TotalAmount:     item.TotalAmount,
			TypeId:          item.TypeId,
			PayFor:          item.PayFor,
			CardId:          item.CardId,
		}); err != nil {
			util.GetLogger().Error("[PayGateway-prePay] - error:%s", err.Error())
		} else {
			paymentId.Encode(ID_Shard, ID_Ver, ID_Payment_Category, payId)
		}
	}
	return
}

func (gp *GatewayPay) doPay(payID ID, payFor int) (*QPayRS, error) {
	var prepo data.PayRepo
	var applyResult *qpay.PaymentResult
	var resendResult *qpay.ResultV2
	var paySubject string

	if payFor == 0 {
		paySubject = "支付订金"
	} else {
		paySubject = "支付运单"
	}

	rs := new(QPayRS)

	_, _, _, pid, err := payID.Decode()
	if err != nil {
		return nil, errors.New(errors.MSG_Pay_Invalid_ID)
	}

	pd, err := prepo.GetPaymentDetailByPayID(int(pid))
	if err != nil {
		return nil, errors.New(errors.MSG_Pay_Error)
	}

	c, err := prepo.GetQPayBankCard(pd.MemberId, pd.CardId)
	if err != nil {
		return nil, errors.New(errors.MSG_Pay_Error)
	}

	ok, q := prepo.GetQPay(int(pid))
	if !ok {
		q = &data.QPayDto{
			ID:       int(pid),
			MemberID: pd.MemberId,
			State:    0,
			Amount:   int(math.Round(float64(pd.DepositAmount * 100))),
		}

		err = prepo.AddQPay(q)
		if err == nil {
			p := qpay.NewPayment()
			p.Subject = paySubject
			p.Amount = fmt.Sprintf("%d", q.Amount)
			p.AgreeID = c.PartnerID
			//p.OrderID = string(payID)
			p.OrderID = "111111111113"
			applyResult, err = p.Apply()
			if applyResult.OK() {
				err = prepo.SetQPayApplyOK(q.ID, applyResult.Thpinfo(), applyResult.Undecoded())
				if err == nil {
					rs.State = 1
				}
			} else {
				prepo.UpdateQPayUndecoded(q.ID, applyResult.Undecoded())
			}
		}
	} else {
		p := qpay.NewPayment()
		p.AgreeID = c.PartnerID
		p.OrderID = "111111111113"
		p.Thpinfo = q.Params
		resendResult, err = p.Resend()
		if resendResult.OK() {
			rs.State = 1
			prepo.UpdateQPayUndecoded(q.ID, resendResult.Undecoded())
		}
	}

	rs.PayID = string(payID)
	if err != nil {
		util.GetLogger().Error("[pg.doPay] - error:%s", err.Error())
		err = errors.New(errors.MSG_Pay_Error)
	}

	return rs, err
}
